﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Ninject;
using SignalR;
using SignalR.Hubs;

namespace VideoChat.Models
{
    public class ChatMessage
    {
        public string RoomKey { get; set; }
        public string SenderKey { get; set; }
        public string SenderName { get; set; }
        public string Content { get; set; }

        private DateTime _date { get; set; }
        public DateTime Date
        {
            get { return _date; }
            set
            {
                _date = value;
                _formattedDate = _date.ToLongDateString() + " " + _date.ToLongTimeString();
            }
        }
        private string _formattedDate;
        public string FormattedDate
        {
            get { return _formattedDate; }
        }
    }

    public class RoomModel
    {
        public string RoomKey { get; set; }

        public string MyPublicKey { get; set; }
        public string MyKey { get; set; }
        public string MyName { get; set; }

        public string CalleePublicKey { get; set; }
        public string CalleeKey { get; set; }
        public string CalleeName { get; set; }
    }

    public class Chat : Hub, IDisconnect, IConnected
    {
        private IUsersStore _store;
        private IUsersStore Store
        {
            get
            {
                if (_store == null)
                {
                    var data = new Data();
                    _store = data.GetUsersStore();
                }
                return _store;
            }
        }

        // Обработка отключения от сервера
        public Task Disconnect()
        {
            // Удаляем пользователя из хранилища
            Store.Remove(Context.ConnectionId);
            // Обновляем список пользователей
            UpdateUsers();
            return Clients.leave(Context.ConnectionId, DateTime.Now.ToString());
        }


        // Обработка подключения к серверу
        public Task Connect()
        {
            UpdateUsers();
            return Clients.joined(Context.ConnectionId, DateTime.Now.ToString());
        }

        // Обработка переподключения к серверу
        public Task Reconnect(IEnumerable<string> groups)
        {
            UpdateUsers();
            return Clients.rejoined(Context.ConnectionId, DateTime.Now.ToString());
        }


        /// <summary>
        /// Метод обновления списка пользователей онлайн на клиентах
        /// </summary>
        public void UpdateUsers()
        {
            Clients[C.MAIN_CHAT_GROUP].OnUpdateUsers(Store.GetList());
        }


        // вызывается клиентом для отправки сообщения
        public void Send(ChatMessage message)
        {
            // Что-то делаем только если сообщение не пустое
            if (message.Content.Length > 0)
            {
                if (!Store.CheckFlooder(Context.ConnectionId))
                {
                    //проставляем дату отправки
                    message.Date = DateTime.Now;
                    // идентификатор отправителя
                    message.SenderKey = Context.ConnectionId;
                    // экранируем пришедший текст во избежание хулиганства
                    message.Content = HttpUtility.HtmlEncode(message.Content);
                    message.SenderName = HttpUtility.HtmlEncode(message.SenderName);
                    // Оповещаем клиентов о новом сообщении
                    Clients[message.RoomKey].OnSend(message);

                    Store.SaveMessage(message);
                }
                else
                {
                    message.Date = DateTime.Now;
                    message.SenderKey = Context.ConnectionId;
                    message.Content = HttpUtility.HtmlEncode(message.Content);
                    message.SenderName = HttpUtility.HtmlEncode(message.SenderName);
                    // Оповещаем флудера о новом сообщении
                    Clients[Context.ConnectionId].OnSend(message);
                }
            }
        }

        // вызывается клиентом для подключения к комнате.
        public void JoinRoom(string roomKey, string userName)
        {
            // Сохраняем описание пользователя только если он в основном чате
            if (roomKey == C.MAIN_CHAT_GROUP)
                Store.Add(new User(Context.ConnectionId, HttpUtility.HtmlEncode(userName)));
            // Возвращаем клиенту его id
            Clients[Context.ConnectionId].OnJoinRoom(Context.ConnectionId);
            // Добавляем пользователя в комнату
            Groups.Add(Context.ConnectionId, roomKey);
            // Опопвещаем клиентов о изменении списка пользователей
            if (roomKey == C.MAIN_CHAT_GROUP)
                UpdateUsers();
        }

        /// <summary>
        /// Метод смены имени (логина)
        /// </summary>
        /// <param name="newName"></param>
        public void Rename(string newName)
        {
            Store.Remove(Context.ConnectionId);
            Store.Add(new User(Context.ConnectionId, HttpUtility.HtmlEncode(newName)));
            UpdateUsers();
        }

        /// <summary>
        /// Метод звонка (набор номера)
        /// </summary>
        /// <param name="recieverKey">Ключ абонента, которому звоним</param>
        /// <param name="senderKey">Ключ звонящего</param>
        /// <param name="senderName">Имя звонящего</param>
        public void Call(string recieverKey, string senderKey, string senderName)
        {
            Clients[recieverKey].OnCall(senderKey, HttpUtility.HtmlEncode(senderName));
        }
        /// <summary>
        /// Метод отклонения звонка
        /// </summary>
        /// <param name="senderKey">Ключ звонящего</param>
        /// <param name="recieverKey">Ключ отклоняющего звонок</param>
        /// <param name="recieverName">Имя отклоняющего звонок</param>
        public void RejectCall(string senderKey, string recieverKey, string recieverName)
        {
            Clients[senderKey].OnRejectCall(recieverKey, HttpUtility.HtmlEncode(recieverName));
        }
        /// <summary>
        /// Принятие звонка
        /// </summary>
        /// <param name="calleePulicKey">Ключ звонившего абонента</param>
        /// <param name="calleeName">Имя звонившего абонента</param>
        /// <param name="myName">Имя принимающего абонента</param>
        public void AcceptCall(string calleePulicKey, string calleeName, string myName)
        {
            string myKey = Guid.NewGuid().ToString().Replace("-", "");
            string calleeKey = Guid.NewGuid().ToString().Replace("-", "");
            string roomKey = Guid.NewGuid().ToString().Replace("-", "");

            var model = new RoomModel
                            {
                                MyPublicKey = Context.ConnectionId,
                                MyKey = myKey,
                                MyName = myName,
                                CalleePublicKey = calleePulicKey,
                                CalleeKey = calleeKey,
                                CalleeName = calleeName,
                                RoomKey = roomKey
                            };

            // Сохраняем информацию о начинающемся сеансе
            Store.SaveRoomInfo(model);
            // Рассылаем уведомления
            Clients[calleePulicKey].OnAcceptCall(false, roomKey);
            Clients[Context.ConnectionId].OnAcceptCall(true, roomKey);
        }
    }

    public class ChatConnectionIdGenerator : IConnectionIdGenerator
    {
        public string GenerateConnectionId(IRequest request)
        {
            if (request.Cookies["key"] != null)
            {
                var cook = request.Cookies["key"];

                if (!String.IsNullOrEmpty(cook.Value))
                {
                    return cook.Value;
                }
            }

            throw new InvalidOperationException("No cookie");
        }
    }
}